#!/usr/bin/env python
import sys
import os
import os.path
from   optparse import OptionParser

res_root = os.path.realpath( os.path.join(os.path.dirname( os.path.realpath( os.path.abspath( __file__))) , "../") )

#-----------------------------------------------------------------

default_lib_list          = ["ecl"   , "res"]
default_define_list       = ["HAVE_PTHREAD" , "COMPILER_GCC"]


CFLAGS       = "-std=gnu99 -O2 -Wall -fpic -g"
LDFLAGS_list = ["-shared"]
CC           = "gcc"
LD           = CC

#-----------------------------------------------------------------


c_file      = 0
header_file = 1
object_file = 2
other       = 3

file_types = {".o" : object_file ,
              ".h" : header_file ,
              ".c" : c_file }

def base_name(file):
    (name,ext) = os.path.split( file )
    return name


def file_type( file ):
    name,ext = os.path.splitext( file )
    return file_types.get( ext , other )


def object_file_name( file ):
    (name,ext) = os.path.splitext( file )
    return "%s.o" % name


def make_LDFLAGS( use_rpath , lib_path_list):
    if use_rpath:
        LDFLAGS_list.append("-Wl,--enable-new-dtags")
        for path in lib_path_list:
            LDFLAGS_list.append("-Wl,-rpath,%s" % path)
    LDFLAGS_list.append("-Wl,-soname,")

    return " ".join(LDFLAGS_list)


def make_XFLAG( X , def_list ):
    FLAG = ""
    for d in def_list:
        FLAG += "-%s%s " % (X , d)
    return FLAG


def compile_file( file , IFLAG , DFLAG , verbose):
    target = object_file_name( file )
    if os.path.exists( target ):
        os.unlink( target )

    cmd = "%s %s %s %s -c %s -o %s" % (CC , CFLAGS , IFLAG , DFLAG , file , target)
    if verbose:
        print "Compiling: %s" % cmd
    os.system( cmd )
    if os.path.exists( target ):
        return target
    else:
        sys.exit("Compile cmd:%s failed" % cmd)


def link( soname , filename , object_list , LDFLAGS , LFLAG , lFLAG , verbose):
    object_string = ""
    for obj in object_list:
        object_string += "%s " % obj

    cmd = "%s %s%s -o %s %s %s %s" % ( LD , LDFLAGS , soname , filename , object_string , LFLAG , lFLAG)
    if verbose:
        print "Linking  : %s" % cmd
    if os.path.exists( filename ):
        os.unlink( filename )
    os.system(cmd)
    if os.path.exists( filename ):
        return True
    else:
        return False


usage = """
The ert_module script is a small convenience script to
compile C source code into an analysis module which can
be loaded by ert/libres. The script is controlled by commandline
arguments:

  1. The first argument should be the name of the module
     you are creating, an extension .so will be appended.

  2. List the source files you want to include, the
     files should have extension .c. In addition you can
     include object files which have been compiled by
     other means, the object files should have
     extension .o

  3. Optionally you can pass -I and -D options which are
     passed to the compiler; and -l and -L options which
     are passed to the linker.

Example:

  ert_module my_module my_src1.c my_src2.c f90_object1.o f90_object2.o -I/path -DFAST=Yes -L/path/to/lib -lfm -lz

Will create a module 'my_module' based on the src files my_src1.c
and my_src2.c; in addition the object files f90_object1.o and
f90_object2.o will be included in the final module.

-----------------------------------------------------------------

To compile the module code you will typically need the include files
and libraries from an existing libres installation. By default the
ert_module script will locate the libres installation based on the
location of the script, but you can pass the option:

  --res-root=/path/where/libres/is/installed

The --res-root option should point to a directory containing the
lib64/ and include/ directories of a binary etr distribution. In
addition to --res-root you can use the normal -L/path/to/lib option to
send in additional link path arguments.

By default the path to shared libraries will not be embedded in the
resulting module, but by passing the option --use-rpath you can tell
the script to embed these paths in the final shared object.

-----------------------------------------------------------------

Options summary:

  -L/path/to/lib: Include the path /path/to/lib in the linker path

  -llib1        : Link with the library lib1

  -I/include : Include the path /include in the compiler include path.

  --res-root=/path/to/libres : Use this is as root for libres headers
           and libraries. [Default: inferred from location of script]

  --use-rpath : Embed library paths in shared objects. Default off.

  --exclude-res: Do not use any default libraries or headers from libres


Default flags:

Compile: %s %s  %s
Link:    %s %s  %s
""" % (CC,
       make_XFLAG( "I" , ["./" , "%s/include" % res_root]) ,
       make_XFLAG( "D" , default_define_list) ,
       LD ,
       make_XFLAG("L" , ["%s/lib64" % res_root]) ,
       make_XFLAG("l" , default_lib_list))

parser = OptionParser( usage )
parser.add_option("--res-root" , dest="res_root" , action="store")
parser.add_option("-I" , dest = "include_path_list", action = "append" , default = [])
parser.add_option("-D" , dest = "define_list"      , action = "append" , default = [])
parser.add_option("-L" , dest = "lib_path_list"    , action = "append" , default = [])
parser.add_option("-l" , dest = "lib_list"         , action = "append" , default = [])
parser.add_option("--exclude-res" , dest = "exclude_res" , action="store_true" , default = False)
parser.add_option("--use-rpath" , dest="use_rpath" , action="store_true" , default = False)
parser.add_option("--silent" , dest="silent" , action="store_true" , default = False)

(options , args) = parser.parse_args()
if len(args) == 0:
    sys.exit( usage )

if options.res_root:
    res_root = options.res_root

if options.exclude_res:
    default_include_path_list = ["./"]
    default_lib_path_list = []
    default_lib_list = []
else:
    default_include_path_list = ["./" , "%s/include" % res_root]
    default_lib_path_list = ["%s/lib64" % res_root]
    default_lib_list = default_lib_list


# What is supplied as commandline options should take presedence, this
# implies that it should be first OR last on the commandline.
include_path_list = options.include_path_list + default_include_path_list
define_list       = default_define_list       + options.define_list
lib_list          = options.lib_list          + default_lib_list
lib_path_list     = options.lib_path_list     + default_lib_path_list


verbose = not options.silent
LDFLAGS = make_LDFLAGS( options.use_rpath , lib_path_list)
input_name = args[0]
(path , tmp ) = os.path.split( input_name )
(module , ext) = os.path.splitext( tmp )

soname = "%s.so" % module
if path:
    filename = "%s/%s.so" % (path , module)
    if not os.path.exists( path ):
        os.makedirs( path )
else:
    filename = "%s.so" % module

#-----------------------------------------------------------------

IFLAG = make_XFLAG( "I" , include_path_list )
DFLAG = make_XFLAG( "D" , define_list )
LFLAG = make_XFLAG( "L" , lib_path_list )
lFLAG = make_XFLAG( "l" , lib_list )

object_list = []
for arg in args[1:]:
    if file_type( arg ) == c_file:
        object_list.append( compile_file( arg , IFLAG , DFLAG , verbose) )
    elif file_type( arg ) == object_file:
        object_list.append( arg )
    else:
        print "** Warning: ignoring file:%s" % arg


if link( soname , filename , object_list , LDFLAGS , LFLAG , lFLAG , verbose):
    sys.exit()
else:
    sys.exit("Creating library failed")
